package com.demo.redpacket.controller;

import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.demo.redpacket.model.RedPacket;
import com.demo.redpacket.model.RedPacketRecord;
import com.google.common.primitives.Doubles;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

// 定义红包的控制器
@RestController
@Slf4j
public class RedPacketController {

    public static final String RED_PACKET_RAIN_KEY = "redpacket:rain:"; //红包雨活动key
    public static final String RED_PACKET_CONSUMER_KEY = "redpacket:consumer:"; //用户是否已经领取
    public static final String RED_PACKET_RECORD_KEY = "redpacket:record:"; //用户领取红包记录

    @Autowired
    private RedisTemplate redisTemplate;

    // 定义发红包的方法
    //totalMoney 红包的总金额
    //totalPeopleNum 参与活动的总人数
    @RequestMapping("/send")
    public String sendRedPacket(int totalMoney, int totalPeopleNum) {
        // 拆红包, 生成红包列表
        // 红包的金额分配问题
        List<RedPacket> redPacketList = this.divideRedPacket(totalMoney, totalPeopleNum);
        // 发红包, 并且把红包列表放入redis
        String redPacketRainId = IdUtil.objectId();
        String key = RED_PACKET_RAIN_KEY + redPacketRainId;
        redisTemplate.opsForList().leftPushAll(key, redPacketList);
        // 设置红包过期时间
        redisTemplate.expire(key, 1, TimeUnit.DAYS); //红包过期未领取，将会返还到余额
        // 发红包结束, 返回前台
        return "红包雨活动id："+redPacketRainId + "，红包列表：" + JSONUtil.toJsonStr(redPacketList);
    }

    // 定义抢红包的方法
    //key：红包雨活动id
    //userId
    @RequestMapping("/receive")
    public String receiveRedPacket(String key, String userId) {
        // 通过 hexists 命令判断红包领取记录防重 Hash 表中用户是否领取过红包 ，若用户未领取过红包，流程继续；
        // hashmap.isExistsKey()
        Boolean isReceived = redisTemplate.opsForHash().hasKey(RED_PACKET_CONSUMER_KEY + key, userId);
        if (!isReceived) {
            // 抢红包, 根据红包雨活动id从redis中获取红包列表 ，缓存预热
            RedPacket redPacket = (RedPacket)redisTemplate.opsForList().rightPop(RED_PACKET_RAIN_KEY + key);
            // 如果不为空, 那么把抢到的红包id放入redis
            if (redPacket != null) {
                //操作红包领取记录防重 Hash 表 ，调用 HSET 命令存储用户领取记录
                redisTemplate.opsForHash().put(RED_PACKET_CONSUMER_KEY + key, userId, redPacket.getRedPacketId());

                // redisTemplate.opsForHash().putAll();

                // redisTemplate.opsForHash().get()
                //打印抢红包信息
                log.info("用户" + userId + "抢到了" + redPacket.getAmount() + "元");
                //将红包领取信息 lpush 进入用户红包领取记录列表
                RedPacketRecord redPacketRecord = new RedPacketRecord(redPacket.getRedPacketId(),redPacket.getAmount(),userId);
                String redPacketRecordKey = RED_PACKET_RECORD_KEY + key;
                redisTemplate.opsForList().leftPushAll(redPacketRecordKey ,redPacketRecord);
                List<RedPacketRecord> redPacketRecordList = redisTemplate.opsForList().range(redPacketRecordKey, 0, -1);
                // 抢红包结束, 返回前台
                return "红包雨活动id："+key+"，用户id："+userId+ "，抢到红包：" + redPacket.getAmount()+"，红包id："+redPacket.getRedPacketId()+"，已领取的红包列表："+ JSONUtil.toJsonStr(redPacketRecordList);
            } else {
                return "红包已经被抢完了";
            }
        }
        return userId + "已经抢过红包了";
    }

    // 拆红包的算法,二倍均值法, 保证每个人都有红包, 返回值为List<Double>
    public List<RedPacket> divideRedPacket(double totalAmount, int totalPeopleNum) {
        List<RedPacket> redPacketList = new ArrayList<>();
        //总金额
        double restAmount = totalAmount;
        //总人数
        int restPeopleNum = totalPeopleNum;
        Random random = new Random();
        RedPacket redPacket = null;
        for (int i = 0; i < totalPeopleNum - 1; i++) {
            // 随机范围：[0.01,剩余人均金额的两倍)，左闭右开
            double amount = random.nextDouble() * 2 * restAmount / restPeopleNum;
            amount = Math.max(0.01, amount);
            restAmount -= amount; //总金额递减
            restPeopleNum--; //参与人数递减
            // amount 保留两位小数
            amount = Doubles.tryParse(String.format("%.2f", amount));
            redPacket = new RedPacket(IdWorker.getIdStr(),amount);
            redPacketList.add(redPacket);
        }
        // 最后一个红包的数据保留两位小数
        restAmount = Doubles.tryParse(String.format("%.2f", restAmount));
        redPacket = new RedPacket(IdWorker.getIdStr(),restAmount);
        redPacketList.add(redPacket);
        return redPacketList;
    }

}
